﻿// http://www.zimms.com
// Copyright (c) 2002-2007
// by James Zimmerman
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in the
// Software without restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so, subject
// to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using Microsoft.Practices.ObjectBuilder;

/// <summary>
/// Concrete implementation of <see cref="IServiceProvider"/> that provided access to the factory capabilities of Object Builder subsystem.
/// </summary>
/// <remarks>
/// <note type="implementnotes">
/// This type provides a simplistic implementation of a bridge to Object
/// Builder. Some features such as contructor literals and lifetime management
/// facilities are not utilized in this version. Type mapping, however, is
/// specifically supported via the <see cref="ObjectBuilderServiceLocator(IEnumerable{TypeMapping})"/>
/// constructor.
/// </note>
/// </remarks>
public class ObjectBuilderServiceLocator : IServiceProvider
{
    #region Fields

    private readonly Builder _builder;
    private readonly Locator _locator;
    private readonly LifetimeContainer _lifetime;

    #endregion

    #region Constructors

    /// <summary>
    /// Intiailizes a new instance of the <see cref="ObjectBuilderServiceLocator"/> class with the default configuration.
    /// </summary>
    public ObjectBuilderServiceLocator()
    {
        this._builder = new Builder();
        this._locator = new Locator();
        this._lifetime = new LifetimeContainer();

        this._locator.Add(typeof(ILifetimeContainer), this._lifetime);

    }

    /// <summary>
    /// Initializes a new instance of the <see cref="ObjectBuilderServiceLocator"/> class with the supplied type mappings.
    /// </summary>
    /// <remarks><paramref name="typeMappings"/> will ignore <see langkeyword="null">null</see> items</remarks>
    /// <param name="typeMappings">The type mappings to create. This value may be <see langkeyword="null">null</see>. Any dictionary value that is <see langkeyword="null">null</see> will be skipped.</param>
    public ObjectBuilderServiceLocator(IEnumerable<TypeMapping> typeMappings)
        : this()
    {
        if (typeMappings == null) return;

        foreach (TypeMapping typeToMap in typeMappings)
        {
            if (typeToMap == null) continue;

            this.CreateTypeMapping(typeToMap);
        }
    }

    #endregion

    #region IServiceProvider Members

    /// <summary>
    /// Gets the service object of the specified type.
    /// </summary>
    /// <param name="serviceType">An object that specifies the type of service object to get.</param>
    /// <returns>A service object of type <paramref name="serviceType"/><p>- or -</p><see langkeyword="null">null</see>
    /// if there is no service object of type <paramref name="serviceType"/>.</returns>
    /// <exception cref="ArgumentNullException"><paramref name="serviceType"/> is <see langkeyword="null">null</see>.</exception>
    /// <exception cref="ArgumentException"><paramref name="serviceType"/> is <see langkeyword="abstract">abstract</see><p>- or -</p>
    /// an <see langkeyword="interface"/> that is not mapped.</exception>
    public virtual Object GetService(Type serviceType)
    {
        if (serviceType == null) throw new ArgumentNullException("serviceType");

        return this._builder.BuildUp(this._locator, serviceType, null, null);
    }

    #endregion

    #region Methods

    /// <summary>
    /// Creates a mapping between two types, one the interface contract type, the other for the type to fulfill the contract.
    /// </summary>
    /// <param name="typeToMap">The mapping to perform.</param>
    /// <exception cref="ArgumentNullException"><paramref name="typeToMap"/> is <see langkeyword="null">null</see>.</exception>
    public void CreateTypeMapping(TypeMapping typeToMap)
    {
        if (typeToMap == null) throw new ArgumentNullException("typeToMap");

        TypeMappingPolicy policy = new TypeMappingPolicy(typeToMap.ReificationType, null);
        this._builder.Policies.Set<ITypeMappingPolicy>(policy, typeToMap.ContractType, null);
    }

    /// <summary>
    /// Assigns the singleton pattern to <typeparamref name="T"/>.
    /// </summary>
    /// <typeparam name="T">The type to be assigned the singleton pattern.</typeparam>
    public void AssignSingleton<T>()
    {
        ISingletonPolicy policy = new SingletonPolicy(true);
        this._builder.Policies.Set(policy, typeof(T), null);
    }

    public virtual void AssignSingleton<T>(T instance)
    {
        PolicyList policies = new PolicyList();
        policies.SetDefault<ISingletonPolicy>(new SingletonPolicy(true));

        this._builder.BuildUp<T>(this._locator, null, instance, policies);
    }

    #endregion
}

